package org.geogebra.desktop.gui.view.spreadsheet;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.ArrayList;
import org.geogebra.common.awt.GPoint;
import org.geogebra.common.gui.view.spreadsheet.MyTable;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoElementSpreadsheet;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.desktop.gui.view.algebra.AlgebraViewD;
import org.geogebra.desktop.main.AppD;
import org.geogebra.desktop.util.AlgebraViewTransferHandler;
/**
* Handles drag and drop for the spreadsheet
*
* @author G. Sturr
*
*/
public class SpreadsheetViewDnD
implements DragGestureListener, DragSourceListener, DropTargetListener {
private AppD app;
private MyTableD table;
// current drag over cell
private GPoint currentCell = new GPoint(0, 0);
// flags
private boolean isTranspose = false;
private boolean isCopyByValue = true;
boolean allowDrop = true;
static DataFlavor HTMLflavor;
static {
try {
HTMLflavor = new DataFlavor("text/html;class=java.lang.String");
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
}
/***************************************
* Constructor
*
* @param app
* @param view
*/
@SuppressWarnings("unused")
public SpreadsheetViewDnD(AppD app, SpreadsheetViewD view) {
this.app = app;
this.table = (MyTableD) view.getSpreadsheetTable();
new DragSource();
// DragGestureRecognizer dgr =
// ds.createDefaultDragGestureRecognizer(table,
// DnDConstants.ACTION_COPY, this);
new DropTarget(table, this);
}
// ===========================================
// Getters/setters
// =============================================
public void setAllowDrop(boolean allowDrop) {
this.allowDrop = allowDrop;
}
public boolean isTranspose() {
return isTranspose;
}
public void setTranspose(boolean isTranspose) {
this.isTranspose = isTranspose;
}
public boolean isCopyByValue() {
return isCopyByValue;
}
public void setCopyByValue(boolean isCopyByValue) {
this.isCopyByValue = isCopyByValue;
}
// ======================================
// Drag Source Listeners
// ======================================
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
if (!table.isOverDnDRegion) {
return;
}
/*
* ----- code from AlgebraView Dnd, to be adapted later
*
* if(geoLabelList == null) geoLabelList = new ArrayList<String>(); else
* geoLabelList.clear();
*
* for(GeoElement geo : app.getSelectedGeos()){
* geoLabelList.add(geo.getLabel()); }
*
* // if we have something ... do the drag! if(geoLabelList.size() > 0){
*
* // create drag image StringBuilder sb = new StringBuilder();
* sb.append("\\fbox{\\begin{array}{l}"); for(GeoElement
* geo:app.getSelectedGeos()){
* sb.append(geo.getLaTeXAlgebraDescription(true)); sb.append("\\\\"); }
* sb.append("\\end{array}}"); ImageIcon ic =
* GeoGebraIcon.createLatexIcon(app, sb.toString(), app.getPlainFont(),
* false, Color.DARK_GRAY, null);
*
* // start drag ds.startDrag(dge, DragSource.DefaultCopyDrop,
* ic.getImage(), new Point(-5,-ic.getIconHeight()+5), new
* TransferableAlgebraView(geoLabelList), this); }
*
*/
}
@Override
public void dragDropEnd(DragSourceDropEvent e) {
// nothing to do
}
@Override
public void dragEnter(DragSourceDragEvent e) {
// nothing to do
}
@Override
public void dragExit(DragSourceEvent e) {
// nothing to do
}
@Override
public void dragOver(DragSourceDragEvent e) {
// nothing to do
}
@Override
public void dropActionChanged(DragSourceDragEvent e) {
// nothing to do
}
// ======================================
// Drop Target Listeners
// ======================================
/**
* Listener that notifies the table to prepare for dragging
*/
@Override
public void dragEnter(DropTargetDragEvent dte) {
table.setTableMode(MyTable.TABLE_MODE_DROP);
}
/**
* Listener that notifies the table to stop handling drag events
*/
@Override
public void dragExit(DropTargetEvent dte) {
table.setTableMode(MyTable.TABLE_MODE_STANDARD);
}
/**
* Listener to keep track of current mouseOver cell and update the target
* cell border
*/
@Override
public void dragOver(DropTargetDragEvent dte) {
GPoint overCell = table.getIndexFromPixel(dte.getLocation().x,
dte.getLocation().y);
// if mouse over a new cell then update currentCell and repaint the
// target cell border
if (!overCell.equals(currentCell)) {
currentCell = overCell;
table.setTargetcellFrame(table.getCellBlockRect(currentCell.x,
currentCell.y, currentCell.x, currentCell.y, true));
table.repaint();
}
}
/**
* Handles drops.
*/
@Override
public void drop(DropTargetDropEvent dte) {
Transferable t = dte.getTransferable();
isTranspose = false;
isCopyByValue = true;
// case(1) algebraViewFlavor
if (t.isDataFlavorSupported(
AlgebraViewTransferHandler.algebraViewFlavor)) {
// if modifier key down, open a dialog to get user drop options
if (app.getShiftDown() || app.getControlDown()) {
DialogCopyToSpreadsheet id = new DialogCopyToSpreadsheet(app,
this);
id.setVisible(true);
}
if (!allowDrop) {
allowDrop = true;
handleDropComplete(dte, false);
return;
}
boolean success = handleHtmlFlavorDrop(dte);
handleDropComplete(dte, success);
return;
}
// case(2) String or HTML flavor
else if (t.isDataFlavorSupported(DataFlavor.stringFlavor)
|| t.isDataFlavorSupported(HTMLflavor)) {
boolean success = table.copyPasteCut.paste(currentCell.x,
currentCell.y, currentCell.x, currentCell.y, t);
handleDropComplete(dte, success);
return;
}
}
/**
* Handles drops from the AlgebraView
*
* @param dte
* @return
*/
private boolean handleHtmlFlavorDrop(DropTargetDropEvent dte) {
Transferable t = dte.getTransferable();
// accept the drop
dte.acceptDrop(dte.getDropAction());
try {
// get list of selected geo labels
ArrayList<String> list;
list = (ArrayList<String>) t.getTransferData(
AlgebraViewTransferHandler.algebraViewFlavor);
// exit if empty list
if (list.size() == 0) {
return false;
}
GeoElement[] geoArray = new GeoElement[list.size()];
for (int i = 0; i < geoArray.length; i++) {
GeoElement geo = app.getKernel().lookupLabel(list.get(i));
if (geo != null) {
if (GeoElementSpreadsheet.hasSpreadsheetLabel(geo)) {
return false;
}
}
geoArray[i] = geo;
}
// create 2D ArrayList of string expressions for the geos
// if copy by value, use geo.toValueString
// else use " =geo.label" or "=Element[ ...
int rowCount = list.size();
int columnCount = 1;
GeoList tempList, tempMatrix;
ArrayList<ArrayList<String>> dataList = new ArrayList<ArrayList<String>>();
for (int geoIndex = 0; geoIndex < list.size(); geoIndex++) {
// list geo
if (geoArray[geoIndex].isGeoList()
&& !((GeoList) geoArray[geoIndex]).isMatrix()) {
tempList = (GeoList) geoArray[geoIndex];
ArrayList<String> currentRow = new ArrayList<String>();
columnCount = Math.max(columnCount, tempList.size());
for (int k = 0; k < tempList.size(); k++) {
if (isCopyByValue) {
currentRow.add(tempList.get(k).toValueString(
StringTemplate.defaultTemplate));
} else {
currentRow.add("=Element["
+ tempList.getLabel(
StringTemplate.defaultTemplate)
+ "," + (k + 1) + "]");
}
}
dataList.add(currentRow);
}
// matrix geo
else if ((geoArray[geoIndex].isGeoList()
&& ((GeoList) geoArray[geoIndex]).isMatrix())) {
tempMatrix = (GeoList) geoArray[geoIndex];
rowCount += tempMatrix.size() - 1;
for (int row = 0; row < tempMatrix.size(); row++) {
tempList = (GeoList) tempMatrix.get(row);
ArrayList<String> currentRow = new ArrayList<String>();
columnCount = Math.max(columnCount, tempList.size());
for (int col = 0; col < tempList.size(); col++) {
if (isCopyByValue) {
currentRow.add(tempList.get(col).toValueString(
StringTemplate.defaultTemplate));
} else {
currentRow.add("=Element["
+ tempMatrix.getLabel(
StringTemplate.defaultTemplate)
+ "," + (row + 1) + "," + (col + 1)
+ "]");
}
}
dataList.add(currentRow);
}
}
// single geo
else {
ArrayList<String> currentRow = new ArrayList<String>();
if (isCopyByValue) {
currentRow.add(geoArray[geoIndex]
.toValueString(StringTemplate.maxPrecision));
} else {
currentRow
.add("=" + geoArray[geoIndex].getLabelSimple());
}
dataList.add(currentRow);
}
}
// create 2D String arrays to hold expressions for the transfer geos
String[][] data = new String[rowCount][columnCount];
String[][] dataTranspose = null;
for (int r = 0; r < rowCount; r++) {
for (int c = 0; c < dataList.get(r).size(); c++) {
data[r][c] = dataList.get(r).get(c);
}
}
// create a transposed array
if (isTranspose) {
dataTranspose = new String[columnCount][rowCount];
for (int r = 0; r < rowCount; r++) {
for (int c = 0; c < columnCount; c++) {
dataTranspose[c][r] = data[r][c];
}
}
}
if (!isTranspose) {
table.copyPasteCut.pasteExternal(data, currentCell.x,
currentCell.y, currentCell.x + columnCount - 1,
currentCell.y + rowCount - 1);
} else {
table.copyPasteCut.pasteExternal(dataTranspose, currentCell.x,
currentCell.y, currentCell.x + rowCount - 1,
currentCell.y + columnCount - 1);
}
return true;
} catch (UnsupportedFlavorException e) {
// e.printStackTrace();
} catch (IOException e) {
// e.printStackTrace();
}
return false;
}
private void handleDropComplete(DropTargetDropEvent dte, boolean success) {
dte.dropComplete(success);
table.setTableMode(MyTable.TABLE_MODE_STANDARD);
}
@Override
public void dropActionChanged(DropTargetDragEvent dte) {
// TODO Auto-generated method stub
}
/**
* Extension of Transferable for exporting AlgegraView selections as a list
* of Geo labels
*/
static class TransferableAlgebraView implements Transferable {
public final DataFlavor algebraViewFlavor = new DataFlavor(
AlgebraViewD.class, "geoLabel list");
private final DataFlavor supportedFlavors[] = { algebraViewFlavor };
private ArrayList<String> geoLabelList;
public TransferableAlgebraView(ArrayList<String> geoLabelList) {
this.geoLabelList = geoLabelList;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return supportedFlavors;
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
if (flavor.equals(algebraViewFlavor)) {
return true;
}
return false;
}
@Override
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException {
if (flavor.equals(algebraViewFlavor)) {
return geoLabelList;
}
throw new UnsupportedFlavorException(flavor);
}
}
public static SpreadsheetViewDnD get(AppD app2,
SpreadsheetViewD spreadsheetView) {
return new SpreadsheetViewDnD(app2, spreadsheetView);
}
}